import { useOperation } from './operation';
import { len, formData2obj } from './common';
import { GET, POST } from './request';
import {
  notification,
} from 'antd';

const __LOGGING__ = true;

// export const dashboardFilter = {
//   add: {
//     in: params => params,
//     out: res => res,
//   },
//   update: {
//     in: params => params,
//     out: res => res,
//   },
//   fetch: {
//     in: params => params,
//     out: res => res,
//   },
//   remove: {
//     in: params => params,
//     out: res => res
//   },
// }

// Lazy load: 理论上该页面不加载一次，是不需要设置这个依赖的！
const __basesMap__ = { // 局部全局变量
  // 'dependedModelName': 'dependingModelName'


  * patch (payload, bases, ctx) {
    const { select } = useOperation(ctx);

    payload = payload || {};

    if (len(bases)) {
      for (const idx in bases) {
        const base = bases[idx];

        if (len(base)) {
          const { field, keypath } = base;
          const keys = keypath.split('.');

          if (len(keys) > 1 && len(keys[0]) > 0) {
            const modelName = keys.shift();;
            const model = yield select(s => s[modelName]);
            const secKeypath = keys.join('.');
            const value = eval("model."+secKeypath);
            
            value && (payload[field] = value);
          }
        }
      }
    }

    return payload;
  }
} 

/**
 * 
 * services
 *    queryAll, 
 *    queryOne, 
 *    queryBy, 
 *    queryPage, 
 *    createOne, 
 *    removeOne, 
 *    updateOne, 
 *    exportPage, 
 *    importPage, 
 *    disableOne, 
 *    resetOne, 
 *    refreshOne, 
 *    refreshAll,
 *    // 审核
 *    adoptOne,
 *    rejectOne,
 * 
 *    downloadTep
 * 
 *    内置方法使用下划线开头
 *    queryAll -> fetch/fetchAll
 *    queryPage -> fetch
 *    
 * 
 * states
 *    额外状态
 * methods
 *    未支持
 * effects
 *    额外效果
 * reducers
 *    额外更新
 * depends
 *    数据依赖
 * bases
 *    入参字段依赖（接口耦合）, 只针对fetch有用
 * filters
 *    update, add, query, remove
 *    过滤器，入参、出参过滤器
 * apis
 *    { name, path, method }
 */
export const createModel = ({
  name, 
  services, 
  states = {},
  effects = {}, 
  reducers = {},
  depends = [],
  bases = [], // {field: 'pid', keypath: 'xiangmu.currentxiangmu.id'}
  filters = {
    add: payload => payload,
    update: payload => payload,
    fetch: payload => payload,
    remove: payload => payload,
  },
  apis = [],
  besides = [], // 基于bases，排除的action，放在这里。
}) => {
  const defaultPageSize = 10;
  const maxPageSize = 999;
  const apiObjs = {};
  if (apis) {
    for (const idx in apis) {
      const api = apis[idx];
      const httpInvoke = api.method == 'POST' ? POST : GET;
      apiObjs[api.name] = function *({
        payload,
        callback,
      }, ctx) {
        const { call, lead } = useOperation(ctx);
        const res = yield call(
          async params => httpInvoke('/api/project/v2/shop/query', params, api.type)
          , payload);
        
        yield lead(res);

        if (callback) callback(res.result);
      }
    }
  }

  return {
    namespace: name,
    state: {
      all: [], // 所有数据

      list: [], // 分页数据
      paging: { pageSize: defaultPageSize, current: 0, total: 0},
      cursor: { index: 0, limit: defaultPageSize },

      searchValues: {}, // 普通筛选参数

      ...states,
    },
    effects: {
      /**
       * 分页：'next', 'last', 'pageIndex'
       * @example
       *    page: {limit: 1000, next_index: -1, total: 43, next: false}
       */
      * fetch({
        payload,
        callback
      }, ctx) {
        const { call, set, put, select, may } = useOperation(ctx);
        let { cursor, searchValues } = yield select(s => s[name]); // 获取模型依赖信息

        ////////////////////////////////////////////////////////////
        // 参数修正

        payload = payload || {};

        const { current } = payload; // 没有current，就加载当前页面

        if (current) { // 1, 2, 3, ...
          cursor = { ...cursor,
            index: 0+defaultPageSize*(current-1)
          };

          delete payload['current'];
        }

        if (filters && filters.fetch) {
          payload = filters.fetch({...payload})
        }

        // 入参字段 - 模型依赖
        if (len(bases)) {
          for (const idx in bases) {
            const base = bases[idx];

            if (len(base)) {
              const { field, keypath } = base;
              const keys = keypath.split('.');

              if (len(keys) > 1 && len(keys[0]) > 0) {
                const modelName = keys.shift();;
                const model = yield select(s => s[modelName]);
                const secKeypath = keys.join('.');
                const value = eval("model."+secKeypath);

                __basesMap__[model] = name;
                
                value && (payload[field] = value);
              }
            }
          }
        }

        // 拿出筛选参数, 注意顺序：payload 最优先, searchValues 次之, model.cursor 次之
        payload = { ...cursor, ...searchValues, ...payload, };

        ////////////////////////////////////////////////////////////
        // 接口请求
        const res = yield call(services.queryPage, payload);

        ////////////////////////////////////////////////////////////
        // 应答处理

        // 从筛选参数里，抽出筛选项参数, 排除 cursor
        const { index, limit, ... rest } = payload;
  
        may(res) && (yield set({
          list: (res.result instanceof Array) ? res.result : (len(res.result) ?  [res.result] : []),
          paging: res.page,
          cursor: cursor,
          searchValues: rest,
        }))


        ////////////////////////////////////////////////////////////
        // 出发其他依赖项

        // 触发depends的model的fetch
        for (const idx in depends) {
          const ele = depends[idx];
          const model = yield select(state => state[ele]);
          
          if (!model || !len(model.list)) {
            yield put({
              type: ele+'/fetch',
              payload
            });

            continue;
          }
        }

        // 触发bases反向依赖的model的fetch
        // TODO:
        




        if (callback) callback(res.result);
      },


      /** 
       * 获取全部记录
       * 
       * 内置方法使用下划线开头
       */
      * fetchAll({
        payload,
        callback,
      },  ctx) {
        const { call, set, may } = useOperation(ctx);

        // 优先使用 queryAll，其次使用 queryPage&index=0&limit=1000
        let res;

        if (services.queryAll) {
          res = yield call(services.queryAll, payload);

          may(res) && (yield set({
            all: (res.result instanceof Array) ? res.result : [res.result],
          }))

        } else {
          payload = { index: 0, limit: maxPageSize };

          res = yield call(services.queryPage, payload);

          may(res) && (yield set({
            list: (res.result instanceof Array) ? res.result : [res.result],
            paging: res.page,
          }))
        }

        if (callback) callback(res.result);
      },

      /**
       * 新增记录
       */
  
      * add({
        payload,
        callback,
      },  ctx) {
        const { call, lead, select } = useOperation(ctx);

        if (filters && filters.add) {
          payload = filters.add(payload, ctx)
        }

        // 入参字段依赖
        if (len(bases)) {

          payload = payload || {};
          for (const idx in bases) {
            const base = bases[idx];

            if (len(base)) {
              const { field, keypath } = base;
              const keys = keypath.split('.');

              if (len(keys) > 1 && len(keys[0]) > 0) {
                const modelName = keys.shift();;
                const model = yield select(s => s[modelName]);
                const secKeypath = keys.join('.');
                const value = eval("model."+secKeypath);
                
                value && (payload[field] = value);
              }
            }
          }
        }

        const res = yield call(services.createOne, payload);
  
        yield lead(res, 'fetch')
  
        if (callback) callback(res.result);
      },
  
      * remove({
        payload,
        callback,
      }, ctx) {
        const { call, lead } = useOperation(ctx);
        const res = yield call(services.removeOne, payload);
  
        yield lead(res, 'fetch')
  
        if (callback) callback(res.result);
      },
  
      * update({
        payload,
        callback,
      }, ctx) {
        const { call, lead } = useOperation(ctx);
        payload = yield filters.update(payload, ctx);

        const res = yield call(services.updateOne, payload);
        
        yield lead(res, 'fetch')
        
        if (callback) callback();
      },

      * export({ payload, callback, }, ctx) {
        const { call, may, ext, select } = useOperation(ctx);

        // 入参字段 - 模型依赖
        if (len(bases)) {

          payload = payload || {};
          for (const idx in bases) {
            const base = bases[idx];

            if (len(base)) {
              const { field, keypath } = base;
              const keys = keypath.split('.');

              if (len(keys) > 1 && len(keys[0]) > 0) {
                const modelName = keys.shift();;
                const model = yield select(s => s[modelName]);
                const secKeypath = keys.join('.');
                const value = eval("model."+secKeypath);

                __basesMap__[model] = name;
                
                value && (payload[field] = value);
              }
            }
          }
        }

        // 入参字段 - 筛选依赖
        const { searchValues } = yield select(s => s[name]); // 获取模型依赖信息

        const res = yield call(services.exportPage, {
          ...searchValues, ...payload
        });
  
        may(res) && ext(res);

        if (callback) callback(res.result);
      },
  

      * import ({
        payload,
        callback,
      }, ctx) {
        const { call, lead, select } = useOperation(ctx);

        __LOGGING__ && console.log('import payload = ', payload);

        // 入参字段依赖
        if (len(bases)) {
          payload = payload || {};

          for (const idx in bases) {
            const base = bases[idx];

            if (len(base)) {
              const { field, keypath } = base;
              const keys = keypath.split('.');

              if (len(keys) > 1 && len(keys[0]) > 0) {
                const modelName = keys.shift();;
                const model = yield select(s => s[modelName]);
                const secKeypath = keys.join('.');
                const value = eval("model."+secKeypath);

                __basesMap__[model] = name;
                
                value && (payload[field] = value);
              }
            }
          }
        }

        const formData = new FormData();

        for (const key in payload) {
          const val = payload[key];
          formData.append(key, val);  
        }

        __LOGGING__ && console.log('import params = ', formData2obj(formData));

        const res = yield call(services.importPage, formData);
        
        yield lead(res, 'fetch')

        if (callback) callback(res.result);
      },

      * get({
        payload,
        callback,
      }, ctx) {
        const { call, lead } = useOperation(ctx);
        const res = yield call(services.queryOne, payload);
        
        yield lead(res)

        if (callback) callback(res.result);
      },

      * adopt ({
        payload,
        callback,
      }, ctx) {
        const { call, lead } = useOperation(ctx);
        const res = yield call(services.adoptOne, payload);
        
        yield lead(res, 'fetch')

        if (callback) callback(res.result);
      },

      * reject ({
        payload,
        callback,
      }, ctx) {
        const { call, lead } = useOperation(ctx);
        const res = yield call(services.rejectOne, payload);
        
        yield lead(res, 'fetch')

        if (callback) callback(res.result);
      },


      * download ({
        payload, callback
      }, ctx) {
        const { call, lead, may, select } = useOperation(ctx);

        // 入参字段 - 模型依赖
        if (len(bases)) {

          payload = payload || {};
          for (const idx in bases) {
            const base = bases[idx];

            if (len(base)) {
              const { field, keypath } = base;
              const keys = keypath.split('.');

              if (len(keys) > 1 && len(keys[0]) > 0) {
                const modelName = keys.shift();;
                const model = yield select(s => s[modelName]);
                const secKeypath = keys.join('.');
                const value = eval("model."+secKeypath);

                __basesMap__[model] = name;
                
                value && (payload[field] = value);
              }
            }
          }
        }
        
        const res = yield call(services.downloadTep, payload);

        if (may(res)) {
          const path = res.result && res.result.path;
          if (path) {
            const link = document.createElement('a');

            link.setAttribute('download', '');
            link.href = path;
            link.click();
          } else {
            notification.error({
              message: '下载失败',
            });
          }
          console.log(res)
        }

        if (callback) callback(res.result);
      },

      ...effects,
      ...apiObjs,
    },
    reducers: {
      all (state, action) {
        return {
          ...state,
          all: action.payload,
        };
      },
      list (state, action) {
        return {
          ...state,
          list: action.payload,
        };
      },
      paging (state, action) {
        return {
          ...state,
          paging: action.payload
        }
      },
      cursor (state, action) {
        return {
          ...state,
          cursor: action.payload
        }
      },
      searchValues (state, action) {
        return {
          ...state,
          searchValues: action.payload
        }
      },
      ...reducers,
    },
  };
};
